Traefik 反向代理與自動 SSL 部署 (v3 版)
1. 核心觀念與架構解析
Traefik 的資料流與配置邏輯由三大核心抽象組成:
[外部請求 (Client)]
│ (Port 80 / 443)
▼
[EntryPoints] ─── (web / websecure: 定義監聽的通訊協定與埠口)
│
▼
[Routers] ─── (根據 Rule 如 Host(`app.example.com`) 解析請求)
│
├─► [Middlewares] ─── (中介處理:如 HTTP轉HTTPS、Basic Auth、Rate Limit)
│
▼
[Services] ─── (轉發至對應的容器 IP 與內部 Port)
│
▼
[後端容器 (Backend App)]
-
靜態配置 (Static Configuration):定義全域基礎設施(EntryPoints、Providers、憑證解析器 ACME)。在啟動時載入,支援 CLI 參數(
command:)或靜態設定檔(traefik.yml)。 -
動態配置 (Dynamic Configuration):定義路由規則(Routers、Services、Middlewares),由 Provider(此處為 Docker Labels)動態注入,容器重啟或異動時無須重啟 Traefik 本體。
-
SSL 憑證保存 (
acme.json):Traefik 整合 ACME 客戶端,所有由 Let's Encrypt 簽發的私鑰與憑證均存放在單一 JSON 檔案中。該檔案權限嚴格限制為600,否則 Traefik 會出於安全考量直接拒絕載入。
2. Traefik v3 核心配置 (Docker Compose)
相較於舊版 v2.5,Traefik v3 帶來了多項現代化與簡化改進:
-
EntryPoints 原生支援 HTTP 轉 HTTPS:不再需要在 Routers 宣告繁瑣的
hostregexpcatch-all 與 RedirectScheme middleware,直接在 EntryPoint 啟用redirections即可全域生效。 -
ACME 挑戰方式互斥:不可同時開啟
httpChallenge與tlsChallenge,選用其中一種即可(一般推薦tlsChallenge,走 443 埠口最簡潔)。 -
安全強化:Dashboard 預設必須以明確的中介軟體驗證(如 BasicAuth)防護,不允許未保護直接暴露。
步驟 1:建立專屬網路與掛載目錄
為避免各專案容器與 Traefik 網路互相干擾,建立一個全域共享的 Docker Bridge 網路:
# 建立 Traefik 專屬共用外部網路
docker network create proxy-network
# 建立放憑證的目錄並初始化 acme.json 權限(務必 600)
mkdir -p /opt/traefik/letsencrypt
touch /opt/traefik/letsencrypt/acme.json
chmod 600 /opt/traefik/letsencrypt/acme.json
# 建立 BasicAuth 帳號密碼(以 admin / secret123 為例,需安裝 apache2-utils 或使用 openssl/htpasswd)
# 格式為: username:hashed_password(注意:YAML 中 $ 必須寫成 $$ 避免轉義)
echo $(htpasswd -nb admin secret123)
# 輸出範例:admin:$apr1$xyz123... -> 寫進 label 時需轉換為 admin:$$apr1$$xyz123...
步驟 2:Traefik 本體 docker-compose.yml (v3 配置)
在 /opt/traefik/docker-compose.yml 中配置:
services:
traefik:
image: traefik:v3.1
container_name: traefik
restart: unless-stopped
security_opt:
- no-new-privileges:true
networks:
- proxy-network
ports:
- "80:80"
- "443:443"
# 若要直通查看 debug/dashboard 可開 8080,生產環境建議透過內部 Router 存取並關閉 8080 Mapping
# - "8080:8080"
volumes:
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- /opt/traefik/letsencrypt/acme.json:/letsencrypt/acme.json
command:
#### 1. API 與儀表板 (Dashboard) ####
- --api.dashboard=true
- --api.insecure=false
#### 2. Log 與除錯記錄 ####
- --log.level=INFO
- --accesslog=true
#### 3. Docker Provider 設定 ####
- --providers.docker=true
- --providers.docker.exposedbydefault=false # 預設不暴露,必須主動加 label 才代理
- --providers.docker.network=proxy-network # 明確指定預設通訊網路
#### 4. EntryPoints (通訊埠口定義) ####
# HTTP (Port 80) - v3 原生內建全域重定向至 HTTPS
- --entrypoints.web.address=:80
- --entrypoints.web.http.redirections.entrypoint.to=websecure
- --entrypoints.web.http.redirections.entrypoint.scheme=https
- --entrypoints.web.http.redirections.entrypoint.permanent=true
# HTTPS (Port 443)
- --entrypoints.websecure.address=:443
#### 5. 憑證解析器 (Let's Encrypt ACME) ####
# 使用 TLS-ALPN-01 挑戰(直接在 443 埠口驗證,無須額外開放檔案目錄)
- --certificatesresolvers.myresolver.acme.tlschallenge=true
- --certificatesresolvers.myresolver.acme.email=your-email@example.com
- --certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json
labels:
- "traefik.enable=true"
# --- Dashboard 路由設定 (HTTPS) ---
- "traefik.http.routers.traefik-dashboard.rule=Host(`traefik.example.com`)"
- "traefik.http.routers.traefik-dashboard.entrypoints=websecure"
- "traefik.http.routers.traefik-dashboard.service=api@internal"
- "traefik.http.routers.traefik-dashboard.tls=true"
- "traefik.http.routers.traefik-dashboard.tls.certresolver=myresolver"
# --- Dashboard 安全防護 (BasicAuth 中介軟體) ---
- "traefik.http.routers.traefik-dashboard.middlewares=auth"
# 注意:密碼字串中的 $ 符號在 compose 中需用 $$ 跳脫
- "traefik.http.middlewares.auth.basicauth.users=admin:$$apr1$$xyz123...YourHash..."
networks:
proxy-network:
external: true